package com.yunhe.abnormal.service.base.impl;

import com.yunhe.abnormal.client.AuthorityClient;
import com.yunhe.abnormal.consts.AlarmConsts;
import com.yunhe.abnormal.domain.base.AlarmNotifyConfig;
import com.yunhe.common.exception.ArgumentErrorException;
import com.yunhe.common.exception.ObjectNotFoundException;
import com.yunhe.common.exception.UniqueConstraintsException;
import com.yunhe.abnormal.mapper.base.AlarmNotifyConfigMapper;
import com.yunhe.common.model.authority.FirmDTO;
import com.yunhe.common.model.authority.StationDTO;
import com.yunhe.common.model.authority.UserDTO;
import com.yunhe.common.model.base.AlarmNotifyConfigDTO;
import com.yunhe.common.model.base.AlarmSmsConfigDTO;
import com.yunhe.common.model.data.AlarmEventDTO;
import com.yunhe.abnormal.repository.base.AlarmNotifyConfigRepository;
import com.yunhe.abnormal.repository.base.AlarmNotifyConfigSession;
import com.yunhe.abnormal.service.base.AlarmNotifyConfigService;
import com.yunhe.abnormal.service.base.AlarmSmsConfigService;
import com.yunhe.abnormal.transaction.MultiTransaction;
import com.yunhe.common.util.DateTimeUtils;
import net.oschina.j2cache.cache.support.util.SpringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 异常通知配置Service
 * @author liuronglei
 */
@Service
public class AlarmNotifyConfigServiceImpl implements AlarmNotifyConfigService {
    @Autowired
    private AlarmNotifyConfigRepository alarmNotifyConfigRepository;
    @Autowired
    private AlarmNotifyConfigSession alarmNotifyConfigSession;
    @Autowired
    private AlarmNotifyConfigMapper mapper;
    @Autowired
    private AlarmSmsConfigService alarmSmsConfigService;
    @Autowired
    private AuthorityClient authorityClient;

    /**
     * 根据用户通知配置判断是否需要发送
     * @param alarmNotifyConfigs 通知配置
     * @param alarmEvent 异常事件
     * @param alarmNotifyTypeEnum 异常通知类型
     */
    private boolean userNeedNotify(List<AlarmNotifyConfigDTO> alarmNotifyConfigs,
                                   AlarmEventDTO alarmEvent,
                                   AlarmConsts.AlarmNotifyType alarmNotifyTypeEnum) {
        for (AlarmNotifyConfigDTO alarmNotifyConfig : alarmNotifyConfigs) {
            if (alarmNotifyTypeEnum.equals(alarmNotifyConfig.getAlarmNotifyType().getName())
                    && alarmNotifyConfig.getAlarmLevel().getId().equals(alarmEvent.getAlarmLevelId())) {
                return AlarmConsts.AlarmNotifyStatus.OPENED.equals(alarmNotifyConfig.getStatus());
            }
        }
        return false;
    }

    /**
     * 根据运营商通知配置判断是否需要发送
     * @param alarmNotifyConfigs 通知配置
     * @param alarmEvent 异常事件
     * @param alarmNotifyTypeEnum 异常通知类型
     */
    private boolean firmNeedNotify(List<AlarmNotifyConfigDTO> alarmNotifyConfigs,
                                   AlarmEventDTO alarmEvent,
                                   AlarmConsts.AlarmNotifyType alarmNotifyTypeEnum) {
        for (AlarmNotifyConfigDTO alarmNotifyConfig : alarmNotifyConfigs) {
            if (alarmNotifyTypeEnum.equals(alarmNotifyConfig.getAlarmNotifyType().getName())
                    && alarmNotifyConfig.getAlarmLevel().getId().equals(alarmEvent.getAlarmLevelId())) {
                if (AlarmConsts.AlarmNotifyStatus.OPENED.equals(alarmNotifyConfig.getStatus())) {
                    if (alarmNotifyConfig.getIsAllStation()) {
                        return true;
                    } else {
                        List<StationDTO> stations = alarmNotifyConfig.getStations();
                        if (stations !=null && !stations.isEmpty()) {
                            List<Long> stationIds = new ArrayList<>();
                            stations.forEach(station -> stationIds.add(station.getId()));
                            return stationIds.contains(alarmEvent.getStationId());
                        } else {
                            return false;
                        }
                    }
                } else {
                    return false;
                }
            }
        }
        return false;
    }

    /**
     * 判断系统当前时间是否在设置的时间范围内
     * @param timeRange 时间范围
     * @param timeZone 时区
     */
    private Boolean whetherSendMessage(Integer timeRange, String timeZone) {
        String dtime = getDtimeByTimeRange(timeRange);
        Boolean result = false;
        //获取当前时间对应时区的时间戳
        Date now = DateTimeUtils.asDate(LocalDateTime.now().withYear(1970).withDayOfYear(1));
        DateTimeUtils.updateTimeZoneDate(now, timeZone);
        Long nowTimeStamp = now.getTime();
        String[] timePeriods = dtime.split(",");
        //遍历提醒时间段，如果符合则发送提醒
        for (String timePeriod : timePeriods) {
            String[] timePeriodList = timePeriod.split("-");
            Long start = DateTimeUtils.formatToDate(timePeriodList[0], "HH:mm:ss").getTime();
            Long end = DateTimeUtils.formatToDate(timePeriodList[1], "HH:mm:ss").getTime();
            if (start <= nowTimeStamp || nowTimeStamp <= end) {
                result = true;
            }
        }
        return result;
    }

    /**
     * 根据时间范围获得时间字符串
     * (00:00:00-24:00:00),(08:00:00-20:00:00),(00:00:00-08:00:00,20:00:00-24:00:00)
     * @param timeRange 时间范围
     */
    private String getDtimeByTimeRange(Integer timeRange) {
        String dtime = "";
        switch(timeRange) {
            case 1 :
                dtime = "00:00:00-24:00:00";
                break;
            case 2 :
                dtime = "08:00:00-20:00:00";
                break;
            case 3 :
                dtime = "00:00:00-08:00:00,20:00:00-24:00:00";
                break;
        }
        return dtime;
    }

    /**
     * 获得用于保存时使用的异常通知配置对象（去掉关系，只保存属性）
     * @param alarmNotifyConfig 异常通知配置对象
     */
    private AlarmNotifyConfig getStoreAlarmNotifyConfig(AlarmNotifyConfig alarmNotifyConfig) {
        AlarmNotifyConfig storeAlarmNotifyConfig = new AlarmNotifyConfig();
        storeAlarmNotifyConfig.setId(alarmNotifyConfig.getId());
        storeAlarmNotifyConfig.setStatus(alarmNotifyConfig.getStatus());
        storeAlarmNotifyConfig.setIsAllStation(alarmNotifyConfig.getIsAllStation());
        storeAlarmNotifyConfig.setType(alarmNotifyConfig.getType());
        storeAlarmNotifyConfig.setSn(alarmNotifyConfig.getSn());
        if (alarmNotifyConfig.getFirm() != null) {
            storeAlarmNotifyConfig.setRelatedFirmId(alarmNotifyConfig.getFirm().getId());
        }
        if (alarmNotifyConfig.getUser() != null) {
            storeAlarmNotifyConfig.setRelatedUserId(alarmNotifyConfig.getUser().getId());
        }
        return storeAlarmNotifyConfig;
    }

    /**
     * 新增异常通知配置对象
     * @param alarmNotifyConfig 异常通知配置对象
     */
    private AlarmNotifyConfigDTO createOne(AlarmNotifyConfigDTO alarmNotifyConfig) throws ArgumentErrorException, ObjectNotFoundException, UniqueConstraintsException {
        AlarmNotifyConfig myConfig = mapper.from(alarmNotifyConfig);
        AlarmNotifyConfig newConfig = alarmNotifyConfigRepository.save(getStoreAlarmNotifyConfig(myConfig));
        myConfig.setId(newConfig.getId());
        alarmNotifyConfigSession.unBindRelat(myConfig.getId());
        alarmNotifyConfigSession.bindRelat(myConfig);
        return mapper.to(myConfig);
    }

    @Override
    @MultiTransaction
    public AlarmNotifyConfigDTO create(AlarmNotifyConfigDTO alarmNotifyConfig) throws ArgumentErrorException, ObjectNotFoundException, UniqueConstraintsException {
        return createOne(alarmNotifyConfig);
    }

    @Override
    @MultiTransaction
    public List<AlarmNotifyConfigDTO> create(List<AlarmNotifyConfigDTO> alarmNotifyConfigs) throws ArgumentErrorException, ObjectNotFoundException, UniqueConstraintsException {
        List<AlarmNotifyConfigDTO> result = new ArrayList<>();
        for (AlarmNotifyConfigDTO alarmNotifyConfig : alarmNotifyConfigs) {
            result.add(createOne(alarmNotifyConfig));
        }
        return result;
    }

    @Override
    public AlarmNotifyConfigDTO findById(Long id) {
        return mapper.to(alarmNotifyConfigRepository.findById(id).orElse(null));
    }

    @Override
    public void deleteById(Long id) {
        alarmNotifyConfigRepository.deleteById(id);
    }

    @Override
    @MultiTransaction
    public AlarmNotifyConfigDTO updateStatus(Long id, Integer status) throws ArgumentErrorException, ObjectNotFoundException, UniqueConstraintsException {
        AlarmNotifyConfig alarmNotifyConfig = alarmNotifyConfigRepository.findById(id).orElse(null);
        if (alarmNotifyConfig == null) {
            throw new ObjectNotFoundException("找不到ID为" + id + "的异常通知配置");
        }
        if (status != null) {
            alarmNotifyConfig.setStatus(status);
            alarmNotifyConfig = alarmNotifyConfigRepository.save(alarmNotifyConfig);
        }
        return mapper.to(alarmNotifyConfig);
    }

    @Override
    @MultiTransaction
    public AlarmNotifyConfigDTO updateIsAllStation(Long id, Boolean isAllStation) throws ArgumentErrorException, ObjectNotFoundException, UniqueConstraintsException {
        AlarmNotifyConfig alarmNotifyConfig = alarmNotifyConfigRepository.findById(id).orElse(null);
        if (alarmNotifyConfig == null) {
            throw new ObjectNotFoundException("找不到ID为" + id + "的异常通知配置");
        }
        if (isAllStation != null) {
            alarmNotifyConfig.setIsAllStation(isAllStation);
            alarmNotifyConfig = alarmNotifyConfigRepository.save(alarmNotifyConfig);
        }
        return mapper.to(alarmNotifyConfig);
    }

    @Override
    @MultiTransaction
    public AlarmNotifyConfigDTO updateStation(Long id, List<Long> stationIds) throws ArgumentErrorException, ObjectNotFoundException, UniqueConstraintsException {
        AlarmNotifyConfig alarmNotifyConfig = alarmNotifyConfigRepository.findById(id).orElse(null);
        if (alarmNotifyConfig == null) {
            throw new ObjectNotFoundException("找不到ID为" + id + "的异常通知配置");
        }
        alarmNotifyConfigRepository.unbindStation(id);
        if (stationIds != null) {
            for (Long stationId : stationIds) {
                alarmNotifyConfigRepository.bindStation(id, stationId);
            }
        }
        return mapper.to(alarmNotifyConfig);
    }

    @Override
    @MultiTransaction
    public void batchDelete(List<Long> ids) {
        if (ids !=null && !ids.isEmpty()) {
            for (Long id : ids) {
                alarmNotifyConfigRepository.deleteById(id);
            }
        }
    }

    @Override
    @MultiTransaction
    public void deleteByUserId(Long userId) {
        if (userId != null) {
            // 删除异常通知配置
            alarmNotifyConfigSession.deleteNotifyConfigByUserId(userId);
            // 删除异常通知短信配置
            alarmNotifyConfigSession.deleteSmsConfigByUserId(userId);
        }
    }

    @Override
    @MultiTransaction
    public void deleteByFirmId(Long firmId) {
        if (firmId != null) {
            alarmNotifyConfigSession.deleteByFirmId(firmId);
        }
    }

    @Override
    public List<AlarmNotifyConfigDTO> search(Integer type, Long alarmLevelId, String alarmNotifyTypeName, Long firmId, Long userId) {
        return mapper.to(alarmNotifyConfigSession.search(type, alarmLevelId, alarmNotifyTypeName, firmId, userId));
    }

    @Override
    public List<AlarmSmsConfigDTO> searchSmsConfig(Long id) {
        return alarmSmsConfigService.search(id);
    }

    @Override
    @MultiTransaction
    public void updateSmsConfig(Long id, List<AlarmSmsConfigDTO> alarmSmsConfigs) {
        alarmSmsConfigService.update(id, alarmSmsConfigs);
    }

    @Override
    public boolean needNotifyByUser(AlarmEventDTO alarmEvent,
                                    AlarmConsts.AlarmNotifyType alarmNotifyType,
                                    Long userId,
                                    Long firmId) {
        List<AlarmNotifyConfigDTO> userAlarmNotifyConfigs = SpringUtil.getBean(AlarmNotifyConfigService.class)
                .search(AlarmConsts.AlarmNotifyConfigType.USER.value(), alarmEvent.getAlarmLevelId(),
                        alarmNotifyType.value(), null, userId);
        List<AlarmNotifyConfigDTO> firmAlarmNotifyConfigs = SpringUtil.getBean(AlarmNotifyConfigService.class)
                .search(AlarmConsts.AlarmNotifyConfigType.FIRM.value(), alarmEvent.getAlarmLevelId(),
                        alarmNotifyType.value(), firmId, null);
        boolean userNeedNotify = userNeedNotify(userAlarmNotifyConfigs, alarmEvent, AlarmConsts.AlarmNotifyType.POPUP);
        return userNeedNotify && firmNeedNotify(firmAlarmNotifyConfigs, alarmEvent, AlarmConsts.AlarmNotifyType.POPUP);
    }

    @Override
    public List<UserDTO> needNotifyByEvent(AlarmEventDTO alarmEvent) {
        Long stationId = alarmEvent.getStationId();
        StationDTO station = authorityClient.getStationById(stationId).getBody().getResults();
        FirmDTO firm = station.getOperator();
        if (firm != null) {
            List<AlarmNotifyConfigDTO> alarmNotifyConfigs = SpringUtil.getBean(AlarmNotifyConfigService.class)
                    .search(AlarmConsts.AlarmNotifyConfigType.FIRM.value(), alarmEvent.getAlarmLevelId(),
                            AlarmConsts.AlarmNotifyType.SMS.value(), firm.getId(), null);
            for (AlarmNotifyConfigDTO alarmNotifyConfig : alarmNotifyConfigs) {
                if (AlarmConsts.AlarmNotifyType.SMS.equals(alarmNotifyConfig.getAlarmNotifyType().getName())
                        && alarmNotifyConfig.getAlarmLevel().getId().equals(alarmEvent.getAlarmLevelId())) {
                    if (AlarmConsts.AlarmNotifyStatus.OPENED.equals(alarmNotifyConfig.getStatus())) {
                        // 先判断是否需要发送消息
                        boolean needNotify;
                        if (alarmNotifyConfig.getIsAllStation()) {
                            needNotify = true;
                        } else {
                            List<StationDTO> stations = alarmNotifyConfig.getStations();
                            if (stations !=null && !stations.isEmpty()) {
                                List<Long> stationIds = new ArrayList<>();
                                stations.forEach(item -> stationIds.add(item.getId()));
                                needNotify = stationIds.contains(stationId);
                            } else {
                                needNotify = false;
                            }
                        }
                        // 如果需要发送消息，再判断哪些人要发送
                        if (needNotify) {
                            List<AlarmSmsConfigDTO> alarmSmsConfigs = SpringUtil.getBean(AlarmNotifyConfigService.class)
                                    .searchSmsConfig(alarmNotifyConfig.getId());
                            if (alarmSmsConfigs !=null && !alarmSmsConfigs.isEmpty()) {
                                List<UserDTO> users = new ArrayList<>();
                                for (AlarmSmsConfigDTO alarmSmsConfig : alarmSmsConfigs) {
                                    UserDTO user = alarmSmsConfig.getUser();
                                    if (whetherSendMessage(alarmSmsConfig.getTimeRange(), station.getTimeZone()) && user != null) {
                                        List<AlarmNotifyConfigDTO> userAlarmNotifyConfigs = SpringUtil.getBean(AlarmNotifyConfigService.class)
                                                .search(AlarmConsts.AlarmNotifyConfigType.USER.value(), alarmEvent.getAlarmLevelId(),
                                                        AlarmConsts.AlarmNotifyType.SMS.value(), null, user.getId());
                                        boolean userNeedNotify = userNeedNotify(userAlarmNotifyConfigs, alarmEvent, AlarmConsts.AlarmNotifyType.SMS);
                                        if (userNeedNotify) {
                                            users.add(user);
                                        }
                                    }
                                }
                                return users;
                            }
                        }
                    }
                }
            }
        }
        return null;
    }
}
